/// <reference name="MicrosoftAjax.debug.js" />
/// <reference path="../../Resources/js/jquery/jquery.debug.js" />
/// <reference path="../Root.js" />

function MItem(node, basis) {
    /// <field name="node" domElement="true">
    /// The node that the item is bound to.
    /// </field>
    /// <field name="basis" type="MBasis">
    /// The basis of the item
    /// </field>        
    MItem.initializeBase(this, []);
    this.node = node;
    this.node._item = this;
    $(this.node).click(Function.createDelegate(this, this.itemClicked));
    this.descriptor = null;
    if (basis) {
        this.basis = basis;
    }
    else {
        this.basis = MBasis.getById(node.id);
    }
    this.addOnValidationError(Function.createDelegate(this, this.notifyRootValidatorAboutError));
    this.addOnValidationSuccess(Function.createDelegate(this, this.notifyRootValidatorAboutSuccess));
    this.last = this.basis.last;
    this.recountAdditions();
}

MItem.Element = "El";
MItem.Optional = "Op";
MItem.Required = "Rq";
MItem.Leaf = "Lf";
MItem.ChoiceContainer = "CC";
MItem.AdditionsData = "AD";
MItem.Marker = "Mk";
MItem.ButtonContainer = "BC";

MItem.ViewMode = "View";
MItem.EditMode = "Edit";

MItem.prototype = {
    addOnValidationError: function(handler) {
        /// <summary>
        /// Add an event handler for the OnValidationError event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('OnValidationError', handler);
    },

    removeOnValidationError: function(handler) {
        /// <summary>
        /// Remove an event handler from the OnValidationError event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('OnValidationError', handler);
    },

    raiseOnValidationError: function(eventArgs) {
        /// <summary>
        /// Raise the OnValidationError event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the OnValidationError event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('OnValidationError');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addDescriptionChanged: function(handler) {
        /// <summary>
        /// Add an event handler for the DescriptionChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('DescriptionChanged', handler);
    },

    removeDescriptionChanged: function(handler) {
        /// <summary>
        /// Remove an event handler from the DescriptionChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('DescriptionChanged', handler);
    },

    raiseDescriptionChanged: function(eventArgs) {
        /// <summary>
        /// Raise the DescriptionChanged event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the DescriptionChanged event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('DescriptionChanged');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addOnValidationSuccess: function(handler) {
        /// <summary>
        /// Add an event handler for the OnValidationSuccess event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('OnValidationSuccess', handler);
    },

    removeOnValidationSuccess: function(handler) {
        /// <summary>
        /// Remove an event handler from the OnValidationSuccess event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('OnValidationSuccess', handler);
    },

    raiseOnValidationSuccess: function(eventArgs) {
        /// <summary>
        /// Raise the OnValidationSuccess event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the OnValidationSuccess event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('OnValidationSuccess');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addLoading: function(handler) {
        /// <summary>
        /// Add an event handler for the Loading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('loading', handler);
    },

    removeLoading: function(handler) {
        /// <summary>
        /// Remove an event handler from the Loading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('loading', handler);
    },

    raiseLoading: function(eventArgs) {
        /// <summary>
        /// Raise the Loading event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Loading event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('loading');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addUnloading: function(handler) {
        /// <summary>
        /// Add an event handler for the Unloading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('unloading', handler);
    },

    removeUnloading: function(handler) {
        /// <summary>
        /// Remove an event handler from the Unloading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('unloading', handler);
    },

    raiseUnloading: function(eventArgs) {
        /// <summary>
        /// Raise the Unloading event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Unloading event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('unloading');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addUnloaded: function(handler) {
        /// <summary>
        /// Add an event handler for the Unloaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Unloaded', handler);
    },

    removeUnloaded: function(handler) {
        /// <summary>
        /// Remove an event handler from the Unloaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('Unloaded', handler);
    },

    raiseUnloaded: function(eventArgs) {
        /// <summary>
        /// Raise the Unloaded event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Unloaded event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('Unloaded');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addFocusing: function(handler) {
        /// <summary>
        /// Add an event handler for the Focusing event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />        
        this.get_events().addHandler('focusing', handler);
    },

    removeFocusing: function(handler) {
        /// <summary>
        /// Remove an event handler from the Focusing event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('focusing', handler);
    },

    raiseFocusing: function(eventArgs) {
        /// <summary>
        /// Raise the Focusing event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Focusing event
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('focusing');
        if (handler) {
            handler(this, eventArgs);
        }
        var parentItem = this.getParentItem();
        if (parentItem) {
            parentItem.raiseFocusing();
        }
    },

    itemClicked: function(clickEvent) {
        /// <summary>
        /// shows appropriate buttons for the item
        /// </summary>
        clickEvent.stopPropagation();
        this.click();
    },

    click: function() {
        if (BM.TabButtons == null) return;
        var tabButtons = BM.TabButtons.get();
        if (tabButtons == null) return;
        tabButtons.update(this);
    },

    addChanged: function(handler) {
        /// <summary>
        /// Add an event handler for the Changed event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />        
        this.get_events().addHandler('changed', handler);
    },

    removeChanged: function(handler) {
        /// <summary>
        /// Remove an event handler from the Changed event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('changed', handler);
    },

    raiseChanged: function(eventArgs) {
        /// <summary>
        /// Raise the Changed event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Changed event
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('changed');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addUsageChanged: function(handler) {
        /// <summary>
        /// Add an event handler for the UsageChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />        
        this.get_events().addHandler('UsageChanged', handler);
    },

    removeUsageChanged: function(handler) {
        /// <summary>
        /// Remove an event handler from the UsageChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('UsageChanged', handler);
    },

    raiseUsageChanged: function(eventArgs) {
        /// <summary>
        /// Raise the UsageChanged event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the UsageChanged event
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('UsageChanged');
        if (handler) {
            handler(this, eventArgs);
        }
        var children = this.getChildren();
        for (var i = 0; i < children.length; i++) {
            children[i].raiseUsageChanged(eventArgs);
        }
    },

    notifyRootValidatorAboutError: function(source, args) {
        /// <summary>
        /// informs the root validator that the item has errors
        /// </summary>
        var validationError = args[0];
        this.getMForm().getValidator().notifyAboutError(validationError);
    },

    notifyRootValidatorAboutSuccess: function(source, args) {
        /// <summary>
        /// informs the root validator that the item has no errors
        /// </summary>
        this.getMForm().getValidator().notifyAboutSuccess(this);
    },

    getDescriptor: function() {
        /// <summary>
        /// returns an object responsible for descriptions
        /// </summary>
        /// <returns type="BM.Descriptor" />
        if (this.descriptor == null) {
            this.descriptor = new BM.DefaultDescriptor(this);
        }
        return this.descriptor;
    },

    getDescription: function() {
        /// <summary>
        /// returns the output of the descriptor object
        /// </summary>
        /// <returns type="String" />
        return this.getDescriptor().getDescription();
    },

    load: function() {
        /// <summary>
        /// fires all the attached loading handlers and then does the same with its children
        /// </summary>        
        this.raiseLoading();
        var children = this.getChildren();
        for (var i = 0; i < children.length; i++) {
            children[i].load();
        }
    },

    unload: function() {
        /// <summary>
        /// fires all the attached unloading handlers and then does the same with its children
        /// </summary>            
        this.raiseUnloading();
        var children = this.getChildren();
        for (var i = 0; i < children.length; i++) {
            children[i].unload();
        }
    },

    dispose: function() {
        MItem.callBaseMethod(this, 'dispose');
        if (this.node) {

            delete this.node;
        }
        delete this.basis;
    },

    isInUse: function() {
        /// <returns type="Boolean >
        /// returns information if the current item is in use 
        ///(it is not in use if i.e. it is one of the choices that is not selected)
        /// </returns>
        var parentItem = this.getParentItem();

        if (parentItem == null) {
            return true;
        }

        if (ChoiceItem.isInstanceOfType(parentItem)) {
            var parentSelectedItems = parentItem.getSelectedItems();
            if (parentSelectedItems == null) return false;
            if (!Array.contains(parentSelectedItems, this)) return false;
        }

        return parentItem.isInUse();
    },

    getMode: function() {
        /// <returns type="String" >
        /// the MItem mode, can be Edit or View
        /// </returns>
        return this.node.getAttribute('mode');
    },

    getMForm: function() {
        /// <summary>
        /// returns the root MForm object
        /// </summary>    
        /// <returns type="MForm" >
        /// the root MForm object
        /// </returns>
        return this.basis.getMForm();
    },

    getNode: function() {
        /// <summary>
        /// returns the node associated with the item
        /// </summary>    
        /// <returns domElement="true" >
        /// the associated node
        /// </returns>        
        return this.node;
    },

    getParentItemId: function() {
        /// <returns type="String" >
        /// returns the parent item id
        /// </returns>
        var path = new Path(this.getId());
        return path.getParentPath();
    },

    getParentItem: function() {
        /// <returns type="MItem" >
        /// returns the parent item
        /// </returns>
        return MItem.getById(this.getParentItemId());
    },

    getChildrenByName: function(name, startingWith) {
        /// <summary>
        /// returns children that match the specified name
        /// </summary>
        /// <param name="name" type="String" mayBeNull="true">
        /// the name of the children, or the beginning of the name of the children
        /// </param>
        /// <param name="startingWith" type="Boolean" >
        /// if true, searches all the children whose name starts with the specified name
        /// </param>
        /// <returns type="Array" elementType="MItem" >
        /// the children
        /// </returns>
        var items = new Array();
        var id = this.getId() + '_' + name.replace(/:/g, '__');
        if (!startingWith) id += '_';
        var cb = this.basis.children;
        for (var i = 0; i < cb.length; i++) {
            items = items.concat(cb[i].getInstances(id));
        }
        return items;
    },

    getChildByName: function(name, startingWith) {
        /// <summary>
        /// returns a child that matches the name
        /// </summary>
        /// <param name="name" type="String" mayBeNull="false">
        /// the name of the child
        /// </param>
        /// <param name="startingWith" type="Boolean" >
        /// if true, searches all the children whose name starts with the specified name
        /// </param>
        /// <returns type="MItem" mayBeNull="true" >
        /// the child
        /// </returns>
        var children = this.getChildrenByName(name, startingWith);
        if (children.length == 0) return null;
        return children[0];
    },

    getChildren: function() {
        /// <summary>
        /// returns all item children of the current item
        /// </summary>
        /// <returns type="Array" elementType="MItem" >
        /// the children
        /// </returns>
        return this.getChildrenByName('', true);
    },

    getButtonContainer: function() {
        /// <returns type="String" >
        /// the container node of buttons
        /// </returns>
        return $(this.node).children("." + MItem.ButtonContainer)[0];
    },

    getChildrenButtons: function(childName) {
        /// <param name="childName" type="String" mayBeNull="true">
        /// if other than null, filters the children nodes to return only the one that reflects adding/setting a child item of name ...
        /// </param>
        /// <returns type="jQuery">
        /// the jQuery array of buttons that are displayed for this item
        /// </returns>
        var bc = this.getButtonContainer();
        if (!bc) return $([]);
        var result = $(bc).children(".Add");
        if (childName != null) {
            result = result.filter("*[id$='" + childName + "_1__Add']");
        }
        return result;
    },

    focus: function() {
        /// <summary>
        /// raises the focusing element on the item and all its parents
        /// and sets focus on the first possible element inside
        /// </summary>
        /// <returns type="Boolean" >
        /// information if setting the focus was successful
        /// </returns>
        this.raiseFocusing();
        var children = this.getChildren();
        if (children.length == 0) {
            this.click();
            return;
        }
        for (var i = 0; i < children.length; i++) {
            if (children[i].focus()) return true;
        }
    },

    getIndex: function() {
        /// <returns type="Number" integer="true" >
        /// the index of the current item
        /// </returns>
        var id = this.getId();
        var underscoreIndex = id.lastIndexOf("_");
        return id.substring(underscoreIndex + 1);
    },

    getSiblings: function() {
        /// <summary>
        /// returns all siblings of the item, with the item itself
        /// </summary>
        /// <returns type="Array" elementType="MItem" >
        /// the children
        /// </returns>
        var siblings = this.basis.getSiblings(this.getId());
        var array = new Array();
        for (var id in siblings) {
            array[array.length] = siblings[id];
        }
        return array;
    },

    getSiblingId: function(index) {
        /// <param name="index" type="Number" integer="true" mayBeNull="true" >
        /// the index of the sibling
        /// </param>
        /// <returns type="String" >
        /// the id of the sibling with the specified index, or if no index, the id of the first sibling
        /// </returns>
        return Path.getSiblingId(this.getId(), index);
    },

    getId: function(modifier) {
        /// <param name="modifier" type="String" mayBeNull="true" >
        /// the suffix of the id
        /// </param>
        /// <returns type="String" >
        /// the id of the item, or if modifier specified, the id of the items' modifier element (e.g. the marker, or the add button)
        /// </returns>
        var id = this.node.id;
        if (modifier) {
            if (modifier == "Add" || modifier == MItem.Marker) {
                return this.getSiblingId() + "__" + modifier;
            }
            return id + "__" + modifier;
        }
        return id;
    },

    getName: function() {
        /// <returns type="String" >
        /// the descriptive name of the item
        /// </returns>
        return this.node.getAttribute('name');
    },

    getQualifiedName: function() {
        var lastPart = Path.getLast(this.getId());
        var underscoreIndex = lastPart.lastIndexOf("_");
        lastPart = lastPart.substring(0, underscoreIndex);
        return lastPart.replace(/__/, ':');
    },

    getMaxOccurs: function() {
        /// <returns type="Number" integer="true" >
        /// the maximal occurs of the items' siblings
        /// </returns>
        return this.basis.maxOccurs;
    },

    getMinOccurs: function() {
        /// <returns type="Number" integer="true" >
        /// the minimal occurs of the items' siblings
        /// </returns>
        return this.basis.minOccurs;
    },

    setRequired: function() {
        /// <summary>
        /// changes the css class of the node to denote it is required
        /// </summary>
        /// <returns />

        $(this.node).removeClass(MItem.Optional).addClass(MItem.Required);
    },

    setOptional: function() {
        /// <summary>
        /// changes the css class of the node to denote it is optional
        /// </summary>
        /// <returns />
        $(this.node).removeClass(MItem.Required).addClass(MItem.Optional);
    },

    isOptional: function() {
        /// <summary>
        /// checks if the item is optional
        /// </summary>
        /// <returns />
        return $(this.node).hasClass(MItem.Optional);
    },

    getChildMarker: function(id) {
        return $get(Path.getSiblingId(id) + "__" + MItem.Marker);
    },

    getMarker: function() {
        /// <returns domElement="true" >
        /// the marker showing the end of sibling elements
        /// </returns>
        return $get(this.getId(MItem.Marker));
    },

    hasMarker: function() {
        /// <returns type="Boolean" >
        /// does the marker exist
        /// </returns>
        return (this.getMarker() != null);
    },

    show: function(speed) {
        /// <summary>
        /// shows the corresponding node
        /// </summary>
        /// <param name="speed" type="String" mayBeNull="true">
        /// the speed of showing
        /// </param>
        $(this.node).show(speed);
    },

    hide: function(speed) {
        /// <summary>
        /// hides the corresponding node
        /// </summary>
        /// <param name="speed" type="String" mayBeNull="true">
        /// the speed of hiding
        /// </param>
        $(this.node).hide(speed);
    },

    remove: function() {
        if (!this.hasMarker()) {
            this.hide();
            return true;
        }
        if (this.getSiblings().length <= this.getMinOccurs()) {
            alert(BM.Messages['CannotRemove_MinOccurs'] + ' ' + this.getMinOccurs());
            return false;
        }
        this.unload();
        this.basis.unregister(this.node);
        var button = this.getButton();
        if (button) button.refresh();
        this.removeNode();
        return true;
    },

    removeNode: function() {
        var input = document.createElement('input');
        input.setAttribute('type', 'hidden');
        input.setAttribute('name', this.getId() + '_deleted');
        input.setAttribute('id', this.getId() + '_deleted');
        input.setAttribute('value', 'deleted');
        this.getMForm().get_element().appendChild(input);
        MForm.removeNode(this.getNode());
    },

    has: function(lastPart) {
        return MItem.getById(this.getId() + "_" + lastPart);
    },

    add: function(lastPart) {
        if (this.getMode() == MItem.ViewMode) {
            return;
        }
        var item = this.create(lastPart);
        if (item == null) return false;
        item.load();
        var button = item.getButton();
        if (button) button.refresh();
        item.focus();
        return item;
    },

    create: function(lastPart) {
        var id = this.getId() + "_" + lastPart;
        var basis = MBasis.getById(id);
        if (!basis) return;
        var parentNode = this.getNode();
        var marker = this.getChildMarker(id);

        var index = basis.getAvailableIndex(id);
        if (basis.maxOccurs <= basis.getInstances(this.getId()).length) {
            alert(BM.Messages['CannotAdd_MaxOccurs'] + ' ' + basis.maxOccurs + '; ' + lastPart);
            return;
        }
        id = Path.getReindexedId(id, index);

        var newElem = MItem.cloneNode(basis, id, this.getMForm().get_idSeparator());
        parentNode.insertBefore(newElem, marker);
        basis.incrementAvailableIndex(id);
        basis.register(newElem);
        return MItem.getByNode(newElem);
    },

    getADContainer: function() {
        return $(this.node).children("." + MItem.AdditionsData)[0];
    },

    recountAdditions: function() {
        this.additions = new Array();
        this.constraints = new Array();
        var additionsData = this.getADContainer();
        if (!additionsData) return;
        $(additionsData).children("*[class]").each(
        function(item) {
            var addition = new BM.Addition[this.className](item, this);
            if (this.tagName == "A") item.constraints[item.constraints.length] = addition;
            item.additions[item.additions.length] = addition;
        },
        [this]
    );
    },

    getConstraints: function() {
        /// <returns type="Array" elementType="BM.Constraint" >
        /// returns all the constraints from the additions
        /// </returns>
        return this.constraints;
    },

    getAdditions: function() {
        return this.additions;
    },

    getConstraintValue: function(type) {
        for (var i = 0; i < this.constraints; i++) {
            if (this.constraints[i].getConstraintType() == type) {
                return this.constraints[i].getRestriction();
            }
        }
        return null;
    },

    getValue: function() {
        return null;
    },

    getInput: function() {
        return null;
    },

    isDisabled: function() {
        var children = this.getChildren()
        for (var i = 0; i < children.length; i++) {
            if (!children[i].isDisabled()) return false;
        }
        return true;
    },

    setDisabled: function() {
        var children = this.getChildren()
        for (var i = 0; i < children.length; i++) {
            children[i].setDisabled();
        }
    },

    setEnabled: function() {
        var children = this.getChildren()
        for (var i = 0; i < children.length; i++) {
            children[i].setEnabled();
        }
    },

    validate: function() {
        /// <returns type="Boolean >
        /// returns information if the item and its children are valid.
        /// </returns>
        if (this.isOptional()) {
            return true;
        }

        var children = this.getChildren();
        var isValid = this.validateSelf();
        for (var i = 0; i < children.length; i++) {
            if (!children[i].validate()) {
                isValid = false;
            }
        }
        return isValid;
    },

    validateSelf: function() {
        /// <returns type="Boolean >
        /// returns information if the item constraints are met.        
        /// </returns>
        for (var i = 0; i < this.getConstraints().length; i++) {
            var constraint = this.getConstraints()[i];
            if (!constraint.check()) {
                this.raiseOnValidationError([new BM.ConstraintValidationError(this, constraint)]);
                return false;
            }
        }
        this.raiseOnValidationSuccess([]);
        return true;
    }
}

MItem.prototype.getButton = function() {
    try {
        return Button.get(this);
    }
    catch (err) {
        return null;
    }
}

MItem.registerClass("MItem", Sys.Component);

MItem.cloneNode = function(basis, id, idSeparator) {
    var elem = document.createElement(basis.node.tagName);
    for (var i = 0; i < basis.node.attributes.length; i++) {
        var value = basis.node.attributes[i].nodeValue;
        if (value != null && typeof (value) == 'string') {
            elem.setAttribute(basis.node.attributes[i].name, value);
        }
    }
    elem.setAttribute('id', id);
    elem.className = elem.getAttribute('class');

    var modifiedText = basis.node.innerHTML.replace(new RegExp(basis.node.id, 'g'), elem.id);
    var uniqueBasisId = basis.node.id.replace(/_/g, '\\' + idSeparator).replace(new RegExp('\\\\\\' + idSeparator + '\\\\\\' + idSeparator, 'g'), '__');
    var uniqueElementId = elem.id.replace(/_/g, idSeparator).replace(new RegExp('\\' + idSeparator + '\\' + idSeparator, 'g'), '__');
    elem.innerHTML = modifiedText.replace(new RegExp(uniqueBasisId, 'g'), uniqueElementId);
    return elem;
}

MItem.remove = function(node) {
    ///	<summary>        
    /// removes the MItem in which the specified node is
    ///	</summary>
    if (!node) return;
    if ($(node).hasClass(MItem.Element)) {
        return MItem.getByNode(node).remove();
    }
    if (node.parentNode) {
        return MItem.remove(node.parentNode);
    }
}

MItem.create = function(node, basis) {
    ///	<summary>        
    /// based on the node type returns an appropriate MItem type
    ///	</summary>
    /// <param name="node" domElement="true" mayBeNull="false">
    /// the node to which MItem will be bound
    /// </param>
    /// <param name="basis" type="MBasis" mayBeNull="false">
    /// the item's basis
    /// </param>
    ///	<returns type="MItem" />
    var jqNode = $(node);
    if (jqNode.hasClass(MItem.Leaf)) {
        return new LeafItem(node, basis);
    }
    if (jqNode.hasClass(MItem.ChoiceContainer)) {
        return new ChoiceItem(node, basis);
    }
    return new MItem(node, basis);
}

MItem.getByNode = function(node) {
    ///	<summary>        
    ///		finds the item by bound element
    ///	</summary>
    /// <param name="node" domElement="true" mayBeNull="false">
    /// the node to which MItem is bound
    /// </param>
    ///	<returns type="MItem" />      
    if (!node._item) {
        node._item = MItem.getById(node.id);
    }
    return node._item;
}

MItem.getById = function(id) {
    ///	<summary>        
    ///	finds the item by bound element's id attribute
    ///	</summary>
    /// <param name="id" type="String" mayBeNull="false">
    /// the id attribute of the node to which MItem is bound
    /// </param>
    ///	<returns type="MItem" />      
    if (!id) return null;
    var itemId = Path.getItemId(id);
    var basis = MBasis.getById(id);
    if (!basis) {
        return null;
    }
    return basis.instances[itemId];
}